//
//  IndexData.cpp
//  Chart
//
//  Created by CaoJun on 13-6-9.
//
//

#include "IndexData.h"
#include <cmath>

using namespace std;
using namespace ytx;

IndexData::IndexData(IndexConfig indexConfig, int toFix = 2):
para(indexConfig),
indexType(indexConfig.indexType),
dataType(indexConfig.indexType),
drawType(IndexDrawType::Default),
indexChartType(indexConfig.indexChartType),
parentData(NULL),
changeFlag(true),
indexName(""),
lineReminded("")
{
    this->toFix = toFix;
    this->titleType = indexConfig.type;// VOL #01C8F2
    this->quotationType = LineType::L1d;// DEA #11F0C3 DIF #FF4C94 MACD #FFB400
}

std::pair<double, double> IndexData::getIndexDataMinMax(int startIndex, int endIndex) const
{
    double max = numeric_limits<double>::min(), min = numeric_limits<double>::max();
    if(indexData.size() && indexData[0].size()){
//        int endIndex = startIndex + std::min(showCount, getDataLength());
        for (vector<vector<double> >::const_iterator i = indexData.begin(); i != indexData.end(); ++i) {
            const vector<double> & item = *i;
            if (item.size() <= startIndex) {
                startIndex = 0;
            }
            max = MAX(*(max_element(item.begin() + startIndex, item.begin()+endIndex, Tools::compareForMax)), max);
            min = MIN(*(min_element(item.begin() + startIndex, item.begin()+endIndex, Tools::compareForMin)), min);
        }
    }
    else{
        min = numeric_limits<double>::quiet_NaN();
        max = numeric_limits<double>::quiet_NaN();
    }
    return make_pair(min, max);
}

std::pair<double, double> IndexData::getDataMinMax(int startIndex, int endIndex) const
{
    return getIndexDataMinMax(startIndex, endIndex);
}

bool IndexData::isChange(const Quotation * data, const IndexConfig & indexConfig)
{
    if (!(
          isSameParentData(data) &&
          this->getDataLength() == data->getDataLength() &&
          this->para.eqauls(indexConfig)
    )) {
        changeFlag = true;
        return true;
    }
    changeFlag = false;
    return false;
}

bool IndexData::isChange()
{
    return changeFlag;
}

bool IndexData::isSameParentData(const Quotation * data)
{
    return this->id == data->id && this->quotationType == data->quotationType;
}

void IndexData::clearIfParentDataChanged(const Quotation * data)
{
    if (isSameParentData(data)) {
        // donothing
    }
}

namespace {
    const vector<string> EMPTY_BOTTOM_TEXT;
}

// parentData 可能画预警
const std::vector<std::string> & IndexData::getBottomSideText(SizeType::SizeType sizeType)const{
    if(parentData == NULL){
        return EMPTY_BOTTOM_TEXT;
    }
    return parentData->getBottomSideText(sizeType);
}

vector<pair<double, string> > IndexData::getLeftSideText(int startIndex, int endIndex, const Rectangle & rect, const Style & style) const
{
    vector<pair<double, string> > result;
    double min = rect.getMin();
    double max = rect.getMax();
    int column = rect.getColumn();
    double para  = (max - min) / column;
    int toFixed = Config::getInstance()->chartTemplate.keepIndexFixed();

    for(int i = 0;i<column+1;i++)
    {
        result.push_back(make_pair(Tools::convert2Fix(min + i * para, toFixed), style.sideTextColor));
    }

    return result;
}

std::pair<double, double> IndexData::getAdjustedDataMinMax(int startIndex, int endIndex, const Rectangle & rect) const
{
    pair<double, double> minMax = getLineDataMinMax(startIndex, endIndex);
    double min = minMax.first;
    double max = minMax.second;
    double p = rect.adjustValueByDiff(max - min, Config::getInstance()->style.sideTextAccuracy);
    min -= p;
    max += p;
    return make_pair(min, max);
}

void IndexData::initInfo(Quotation * data){
    id = data->getId();
    quotationType = data->getQuotationType();
    row = data->getRow();
    column = data->getColumn();
    drawingCount = data->drawingCount;
    parentData = data;
}


int IndexData::getDataLength() const
{
    return hasData() ? (int)indexData[0].size() :0;
}

bool IndexData::hasData() const
{
    return (indexData.size() > 0 && indexData[0].size() > 0);
}

bool IndexData::hasData(int index) const
{

    if (hasData())
    {
        int count = (int)indexData[0].size();
        return 0 <= index && index < count;
    }
    else{
        return false;
    }
}

Quotation * IndexData::getParentData() const
{
    return parentData;
}


pair<string, string> IndexData::getTopSideInfoText() const
{
    string color = Config::getInstance()->style.textColor;
    string value = " (";
    vector<double> pvalue = para.value;

    for (vector<double>::iterator it = pvalue.begin(); it < pvalue.end(); ++it) {
        value += ConvertToString((*it));
        if(it != pvalue.end()-1){
            value += ",";
        }
    }

    value += ")";

    return make_pair(value, color);
}

void IndexData::calculate(Quotation * data){}

void IndexData::updateLastest(Quotation * data){}


double IndexData::computeSMA(double x, double y, double n ,double m)
{
    return (x*m + (n-m)*y)/n;
}


vector<double> IndexData::validation(int n, vector<double> data)
{
    int i = 0;
    vector<double> x = data;
    for (; i < n; i++) {
        x[i] = numeric_limits<double>::quiet_NaN();
    }
    return x;
}

vector<double> IndexData::validation(int n, int len)
{
    vector<double> data(len);
    return validation(n, data);
}

vector<vector<double> > IndexData::computeMA(Quotation * quotation, int n, double KLineItem::* p, int startIndex)
{
    vector<int> nList(1,n);
    return computeMA(quotation, nList, p, startIndex);
}

vector<vector<double> > IndexData::computeMA(Quotation * quotation, vector<int> nList, double KLineItem::* p, int startIndex)
{
    double price;//= data->kLineData[0].*p;
    vector<vector<double> > data(nList.size());
    vector<double> sum(nList.size());

    int i = startIndex, j = quotation->getDataLength(), index, n;
    vector<double> itemP = transQuote(quotation, p);

    for (vector<double>::iterator it= sum.begin(); it!= sum.end(); ++it) {
        index = (int)(it-sum.begin());
        n = nList[index];
        (*it) = computeSum(itemP, MAX(0, i - n), i, 0);
    }

    while (i < j) {
        price = quotation->kLineData[i].*p;
        price = std::isnan(price) ? 0 : price;
        computeMA(quotation, price, data, nList, sum, i, p, 0);
        i++;
    }
    return data;
}

vector<vector<double> > & IndexData::computeMA(Quotation * quotation, double price, vector<vector<double> > & data, vector<int> nList, vector<double> & sum, int beginIndex, double KLineItem::* p, int divid = 0)
{
    int i = beginIndex;
    bool dividFlag = divid == 0;

    for(int z = 0; z < nList.size(); z++) {
        sum[z] += price;
        if(dividFlag){
            divid = nList[z];
        }
        if(i > nList[z] - 1) {
            sum[z] -= quotation->kLineData[i - nList[z]].*p;
            data[z].push_back(sum[z] / divid);
        }
        else if(i < nList[z] - 1){
            data[z].push_back(numeric_limits<double>::quiet_NaN());
        }
        else{
            data[z].push_back(sum[z] / divid);
        }
    }
    return data;
}


vector<vector<double> > IndexData::computeMA(vector<double> q, int n, int startIndex)
{
    vector<int> nList(1,n);
    return computeMA(q, nList, startIndex);
}

vector<vector<double> > IndexData::computeMA(vector<double> q, vector<int> nList, int startIndex)
{
    double price;//= data->kLineData[0].*p;
    vector<vector<double> > data(nList.size());
    vector<double> sum(nList.size());
    
    int i = startIndex, j = (int)q.size(), index, n;

    for (vector<double>::iterator it= sum.begin(); it!= sum.end(); ++it) {
        index = (int)(it-sum.begin());
        n = nList[index];
        (*it) = computeSum(q, MAX(0, i - n), i, 0);
    }
    
    while (i < j) {
        price = q[i];
        price = std::isnan(price) ? 0 : price;
        computeMA(q, price, data, nList, sum, i, 0);
        i++;
    }
    return data;
}

vector<vector<double> > & IndexData::computeMA(vector<double> q, double price, vector<vector<double> > & data, vector<int> nList, vector<double> & sum, int beginIndex, int divid)
{
    int i = beginIndex;
    bool dividFlag = divid == 0;
    
    for(int z = 0; z < nList.size(); z++) {
        sum[z] += price;
        if(dividFlag){
            divid = nList[z];
        }
        if(i > nList[z] - 1) {
            sum[z] -= q[i - nList[z]];
            data[z].push_back(sum[z] / divid);
        }
        else if(i < nList[z] - 1){
            data[z].push_back(numeric_limits<double>::quiet_NaN());
        }
        else{
            data[z].push_back(sum[z] / divid);
        }
    }
    return data;
}


void IndexData::computeEMA(vector<double>::const_iterator first, vector<double>::const_iterator last, int period, vector<double>::iterator result, double padding)
{
    // the first EMA is just the closing price
    *result++ = *first++;

    // normal formula takes over
    std::transform(first, last, result - 1, result, EmaFormula(period));
}

double IndexData::computeSum(const vector<double> & data, int begin, int len, double isDivid)
{
    double sum = 0.0;
    int i = begin;
    for (; i < len; i++) {
        sum += std::isnan(data[i]) ? 0 : data[i];
    }
    if (isDivid != 0 && !std::isnan(isDivid))
        sum /= isDivid;
    return sum;
}

void IndexData::cleanData(){
    Quotation::cleanData();
    indexData.clear();
}

//endIndex不含
vector<double> IndexData::transQuote(Quotation * quotation, double KLineItem::* p, int startIndex, int endIndex){
    if (endIndex == -1) {
        endIndex = (int)quotation->getKLineData().size();
    }
    vector<double> ret(endIndex-startIndex);
    int i = startIndex;
    while (i < endIndex) {
        ret[i] = quotation->kLineData[i].*p;
        i++;
    }
    return ret;
}

double IndexData::computeStandardDeviation(const vector<double> & data, int n, int startIndex){
    double ax=0, af=0, close = 0;
    int i = startIndex;
    for(int j=i-n+1;j<=i;j++)
    {
        close = data[j];
        ax += close * close;
        af += close;
    }
    return sqrt((ax*n-af*af)/ n /(n-1));
}

